namespace Plugins.Engine
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using Plugins.Core.Interfaces;
    using Plugins.Engine.Internal;
    using Plugins.Interfaces;
    using Plugins.Library;
    using Plugins.Serializer;

    [Serializable]
    public class PluginConfigurationManager : IPluginConfigurationManagerInternal, IPluginConfigurationManager, IPluginHostService, IPluginContainer<IPluginAssemblyConfiguration>, IPluginConstructible<IPluginConfigurationManagerInternal>, IPluginSerializable
    {
        private List<IPluginAssemblyConfiguration> _loaded = new List<IPluginAssemblyConfiguration>();
        private List<IPluginObject> _objects = new List<IPluginObject>();
        private PluginHostService _pluginHostService = new PluginHostService();
        private Collection<PluginConfigurationData> _plugins = new Collection<PluginConfigurationData>();
        internal static readonly IPluginRuntimeFormatter Formatter = new XmlFormatter();
        internal static readonly IPluginFileSerializer<IPluginConfigurationManagerInternal> XmlFileSerializer = new PluginFileSerializer<IPluginConfigurationManagerInternal>();
        internal static readonly IPluginStringSerializer<IPluginConfigurationManager> XmlStringSerializer = new PluginUnicodeStringSerializer<IPluginConfigurationManager>();

        internal PluginConfigurationManager()
        {
        }

        public bool Add(IPluginAssemblyConfiguration value)
        {
            if (this.Index(value) == -1)
            {
                this._plugins.Add(new PluginConfigurationData(value));
                return true;
            }
            return false;
        }
        //
        public void Clear()
        {
            this._plugins.Clear();
        }

        public bool Contains(IPluginAssemblyConfiguration value)
        {
            return (this.Index(value) != -1);
        }

        //public IPluginHost Create(string hostName, string fileName, bool isDefault)
        //{
        //    return this._pluginHostService.Create(hostName, fileName, isDefault);
        //}

        public IPluginHost Create(Guid value, string hostName, string fileName, bool isDefault)
        {
            return this._pluginHostService.Create(value, hostName, fileName, isDefault);
        }

        public void Destroy(IPluginHost host)
        {
            this._pluginHostService.Destroy(host);
        }

        //public void Destroy(Guid value)
        //{
        //    this._pluginHostService.Destroy(guid);
        //}

        private static int Find(IEnumerable<PluginConfigurationData> list, Predicate<PluginConfigurationData> match)
        {
            int index = -1;
            IEnumerator<PluginConfigurationData> enumerator = list.GetEnumerator();
            while (enumerator.MoveNext())
            {
                index++;
                if (match(enumerator.Current))
                {
                    return index;
                }
            }
            return -1;
        }

        private int Index(IPluginAssemblyConfiguration item)
        {
            return Find(this._plugins, delegate (PluginConfigurationData plugin) {
                return ((IEquatable<IPluginAssemblyConfiguration>) plugin).Equals(item);
            });
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        [SuppressMessage("Microsoft.Design", "CA1031")]
        public void Load<T>(IPluginHost host)
        {
            try
            {
                foreach (PluginConfigurationData plugin in this._plugins)
                {
                    this.Load<T>(host, plugin);
                }
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        [SuppressMessage("Microsoft.Design", "CA1031")]
        public void Load<T>(IPluginHost host, IPluginAssemblyConfiguration plugin)
        {
            try
            {
                IPluginHostInternal _host = host as IPluginHostInternal;
                if (_host != null)
                {
                    IPluginObjectCollection<T> objects = _host.Load<T>(plugin);
                    if (objects != null)
                    {
                        foreach (IPluginObject obj in objects)
                        {
                            this._objects.Add(obj);
                        }
                        this._loaded.Add(plugin);
                    }
                }
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
        }

        public bool Remove(IPluginAssemblyConfiguration value)
        {
            int index = this.Index(value);
            if (index != -1)
            {
                this._plugins.RemoveAt(index);
                return true;
            }
            return false;
        }

        public string Serialize()
        {
            return XmlStringSerializer.Serialize(this, Formatter);
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        [SuppressMessage("Microsoft.Design", "CA1031")]
        public void Unload<T>(IPluginHost host)
        {
            try
            {
                foreach (PluginConfigurationData plugin in this._plugins)
                {
                    this.Unload<T>(host, plugin);
                }
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        [SuppressMessage("Microsoft.Design", "CA1031")]
        public void Unload<T>(IPluginHost host, IPluginAssemblyConfiguration plugin)
        {
            try
            {
                IPluginHostInternal _host = host as IPluginHostInternal;
                if (_host != null)
                {
                    IPluginObjectCollection<T> objects = _host.Unload<T>(plugin);
                    if (objects != null)
                    {
                        foreach (IPluginObject obj in objects)
                        {
                            this._objects.Remove(obj);
                        }
                        this._loaded.Remove(plugin);
                    }
                }
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
        }

        void IPluginConstructible<IPluginConfigurationManagerInternal>.Initialize(IPluginConfigurationManagerInternal obj)
        {
            this._plugins = obj.InternalPlugins;
            this._loaded = obj.InternalLoaded;
            this._objects = obj.InternalObjects;
        }

        public int Count
        {
            get
            {
                return this._plugins.Count;
            }
        }

        public IEnumerable<IPluginAssemblyConfiguration> Loaded
        {
            get
            {
                return this._loaded;
            }
        }

        public IEnumerable<IPluginObject> Objects
        {
            get
            {
                return this._objects;
            }
        }

        public Collection<PluginConfigurationData> Plugins
        {
            get
            {
                return this._plugins;
            }
        }

        public IEnumerable<IPluginAssemblyConfiguration> References
        {
            get
            {
                foreach (PluginConfigurationData plugin in _plugins)
                {
                    yield return plugin;
                }
            }
        }

        List<IPluginAssemblyConfiguration> IPluginConfigurationManagerInternal.InternalLoaded
        {
            get
            {
                return this._loaded;
            }
        }

        List<IPluginObject> IPluginConfigurationManagerInternal.InternalObjects
        {
            get
            {
                return this._objects;
            }
        }

        Collection<PluginConfigurationData> IPluginConfigurationManagerInternal.InternalPlugins
        {
            get
            {
                return this._plugins;
            }
        }

        private class XmlFormatter : PluginTypeSerializer<PluginConfigurationManager>
        {
            public override void AddOverrides(IPluginTypeFormatter formatter)
            {
                AddType(formatter, "configuration");
                AddArray(formatter, "Plugins", "plugins");
                AddArrayItem(formatter, "Plugins", "plugin", typeof(PluginConfigurationData));
                PluginConfigurationData.Formatter.AddOverrides(formatter);
            }
        }
    }
}

